package server

import (
	"context"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/coreos/etcd/clientv3"
	"go.uber.org/zap"
)

// EventMsg report chan message struct
type EventMsg struct {
	Value *clientv3.Event
}

//Etcd etcd struct,report udp listen addr
type Etcd struct {
	Client      *clientv3.Client
	Grant       *clientv3.LeaseGrantResponse
	Addrs       []string
	Dltimeout   int
	ReportTime  int
	TTL         int64
	ReportKey   string
	ReportValue string
	StopC       chan bool
}

//NewEtcd new Etcd
func NewEtcd(Addrs []string, Dltimeout int, ReportTime int, TTL int64, ReportDir string, ReportValue string) *Etcd {
	var ReportKey string
	reportKey, err := GetRegisterKey()
	if err != nil {
		log.Panic("Init", err.Error())
	}
	keylen := len(ReportDir)
	if keylen > 0 && ReportDir[keylen-1] != '/' {
		ReportKey = ReportDir + "/" + reportKey
	} else {
		ReportKey = ReportDir + reportKey
	}

	etcd := &Etcd{
		Addrs:       Addrs,
		Dltimeout:   Dltimeout,
		ReportTime:  ReportTime,
		TTL:         TTL,
		ReportKey:   ReportKey,
		ReportValue: ReportValue,
		StopC:       make(chan bool, 1),
	}
	return etcd
}

// GetRegisterKey get key
func GetRegisterKey() (string, error) {
	host, err := os.Hostname()
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%s-%d", host, os.Getpid()), nil
}

// Start start etcd report thread
func (etcd *Etcd) Start() {
	etcd.Init()
	go etcd.PutWork()
}

// Close stop etcd report
func (etcd *Etcd) Close() {
	etcd.StopC <- true
	time.Sleep(1 * time.Second)

	// close channel
	close(etcd.StopC)
}

// Init init Etcd
func (etcd *Etcd) Init() {
	cfg := clientv3.Config{
		Endpoints:   etcd.Addrs,
		DialTimeout: time.Duration(etcd.Dltimeout) * time.Second,
	}
	client, err := clientv3.New(cfg)
	if err != nil {
		Logger.Fatal("etcd init", zap.Error(err))
	}

	etcd.Client = client

	return
}

// PutWork register stream addr
func (etcd *Etcd) PutWork() {
	for {
		select {
		case <-etcd.StopC:
			log.Println("stop etcd")
			return
		// Timing task
		case <-time.After(time.Duration(etcd.ReportTime) * time.Second):
			etcd.Put(etcd.ReportKey, etcd.ReportValue, etcd.TTL)
			// log.Println("[debug]", "reportKey=", etcd.ReportKey, ",reportValue=", etcd.ReportValue, ",etcd.ReportTime=", etcd.ReportTime)
			break
		}
	}
}

// Put put
func (etcd *Etcd) Put(key, value string, ttl int64) error {
	Grant, err := etcd.Client.Grant(context.TODO(), ttl)
	if err != nil {
		log.Println("[error]", err.Error(), ",ttl=", ttl)
		return err
	}
	_, err = etcd.Client.Put(context.TODO(), key, value, clientv3.WithLease(Grant.ID))
	if err != nil {
		log.Println("[error]", err.Error(), ",key=", key, ",value=", value)
		return err
	}
	return nil
}

// DeleteDir deleteDir
func (etcd *Etcd) DeletaDir() {
	etcd.DeletaDir()
}
